package db;

import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.Environment;
import android.util.Log;

import java.io.File;

import com.example.pos_terminal.R;

/**
 * Created by taiji on 2017/8/10.
 */
public abstract class MySQLiteOpenHelper {
    private static final String TAG = MySQLiteOpenHelper.class.getSimpleName();

    // When true, getReadableDatabase returns a read-only database if it is just being opened.
    // The database handle is reopened in read/write mode when getWritableDatabase is called.
    // We leave this behavior disabled in production because it is inefficient and breaks
    // many applications.  For debugging purposes it can be useful to turn on strict
    // read-only semantics to catch applications that call getReadableDatabase when they really
    // wanted getWritableDatabase.
    //为true时,当数据库句柄已经打开,返回read-only句柄.
    private static final boolean DEBUG_STRICT_READONLY = false;

    private final Context mContext;
    private final String mName;
    private final SQLiteDatabase.CursorFactory mFactory;
    private final int mNewVersion;

    private SQLiteDatabase mDatabase;
    private boolean mIsInitializing;
    private final DatabaseErrorHandler mErrorHandler;

    /**
     * Create a helper object to create, open, and/or manage a database.
     * This method always returns very quickly.  The database is not actually
     * created or opened until one of {@link #getWritableDatabase} or
     * {@link #getReadableDatabase} is called.
     *
     * @param context to use to open or create the database
     * @param name of the database file, or null for an in-memory database
     * @param factory to use for creating cursor objects, or null for the default
     * @param version number of the database (starting at 1); if the database is older,
     *     {@link #onUpgrade} will be used to upgrade the database; if the database is
     *     newer, {@link #onDowngrade} will be used to downgrade the database
     */
    public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        this(context, name, factory, version, null);
    }

    /**
     * Create a helper object to create, open, and/or manage a database.
     * The database is not actually created or opened until one of
     * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
     *
     * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
     * used to handle corruption when sqlite reports database corruption.</p>
     *
     * @param context to use to open or create the database
     * @param name of the database file, or null for an in-memory database
     * @param factory to use for creating cursor objects, or null for the default
     * @param version number of the database (starting at 1); if the database is older,
     *     {@link #onUpgrade} will be used to upgrade the database; if the database is
     *     newer, {@link #onDowngrade} will be used to downgrade the database
     * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
     * corruption, or null to use the default error handler.
     */
    public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version,
                            DatabaseErrorHandler errorHandler) {
        if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);

        mContext = context;
        mName = name;
        mFactory = factory;
        mNewVersion = version;
        mErrorHandler = errorHandler;
    }

    /**
     * Return the name of the SQLite database being opened, as given to
     * the constructor.
     * 返回数据库文件名称
     */
    public String getDatabaseName() {
        return mName;
    }
    /**
     * Create and/or open a database that will be used for reading and writing.
     * The first time this is called, the database will be opened and
     * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
     * called.
     *
     * <p>Once opened successfully, the database is cached, so you can
     * call this method every time you need to write to the database.
     * (Make sure to call {@link #close} when you no longer need the database.)
     * Errors such as bad permissions or a full disk may cause this method
     * to fail, but future attempts may succeed if the problem is fixed.</p>
     *
     * <p class="caution">Database upgrade may take a long time, you
     * should not call this method from the application main thread, including
     * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
     *
     * @throws SQLiteException if the database cannot be opened for writing
     * @return a read/write database object valid until {@link #close} is called
     * 创建可读可写的数据库句柄.这个函数第一次调用时,会调用onCreate方法或者onUpgrade方法.
     */
    public SQLiteDatabase getWritableDatabase() {
        synchronized (this) {
            return getDatabaseLocked(true);
        }
    }

    /**
     * Create and/or open a database.  This will be the same object returned by
     * {@link #getWritableDatabase} unless some problem, such as a full disk,
     * requires the database to be opened read-only.  In that case, a read-only
     * database object will be returned.  If the problem is fixed, a future call
     * to {@link #getWritableDatabase} may succeed, in which case the read-only
     * database object will be closed and the read/write object will be returned
     * in the future.
     *
     * <p class="caution">Like {@link #getWritableDatabase}, this method may
     * take a long time to return, so you should not call it from the
     * application main thread, including from
     * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
     *
     * @throws SQLiteException if the database cannot be opened
     * @return a database object valid until {@link #getWritableDatabase}
     *     or {@link #close} is called.
     *     创建只读数据库句柄.
     */
    public SQLiteDatabase getReadableDatabase() {
        synchronized (this) {
            return getDatabaseLocked(false);
        }
    }

    private SQLiteDatabase getDatabaseLocked(boolean writable) {
        if (mDatabase != null) {
            /*if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
                return mDatabase; // The database is already open for business

            }*/

            if (!mDatabase.isOpen()) {
                // Darn!  The user closed the database by calling mDatabase.close().
                //说明用户手动调用close方法将mDatabase关闭了,需要重启打开
                mDatabase = null;
            } else if (!writable || !mDatabase.isReadOnly()) {
                // The database is already open for business.
                return mDatabase;
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException("getDatabase called recursively");
        }

        SQLiteDatabase db = mDatabase;
        try {
            //打开数据库前将mIsInitializing置为true,结束时置为false.
            mIsInitializing = true;

            if (db != null) {
//                if (writable && db.isReadOnly()) {
//                    db.reopenReadWrite();
//                }
            } else if (mName == null) {
                db = SQLiteDatabase.create(null);
            } else {
                //mName不存在则创建数据库文件
                final String path = getDatabasePath(mName).getPath();
//                db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler);
                try {
                    if (DEBUG_STRICT_READONLY && !writable) {
                        db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler);
                    } else {
                        db = mContext.openOrCreateDatabase(path, 0, mFactory, mErrorHandler);
                    }
                } catch (SQLiteException ex) {
                    if (writable) {
                        throw ex;
                    }
                    Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", ex);
                    db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler);
                }
            }

            onConfigure(db);
//            //获取当前数据库版本号,第一次创建版本号为0
//
//            String sql = "drop table trade_consumption_main";
//            String sql2 = "drop table ara_store_log";
//            String trade_consumption_main = "CREATE TABLE [trade_consumption_main]([id] TEXT NOT NULL PRIMARY KEY,  [restaurant] TEXT , " +
//                    "[mealTime] TEXT, [posMachine] TEXT , [flowNo] TEXT,[consumptionTime] TEXT,[orderType] TEXT,[customer] TEXT,[quantity] DOUBLE,[shouldPaidAmount] DOUBLE,[actualAmount] DOUBLE ,"+
//                    "[discountPercent] DOUBLE,[discountAmount] DOUBLE,[reason] TEXT,[quotaPrice] DOUBLE,[times] INTEGER,[isSettlement] TEXT,[creatorId] TEXT,[creatorName] TEXT," +
//                    "[createTime] TEXT,[isXmlOut] TEXT , [CUSTOMER_ADDRESS] TEXT)";
//            String ara_store_log = "CREATE TABLE [ara_store_log]([id] TEXT NOT NULL PRIMARY KEY, [city] TEXT ,  [education] TEXT ,  [ip] TEXT, " +
//                    " [amount] TEXT , [orderNo] TEXT , [isSyn] TEXT NOT NULL, [storeUserId] TEXT , [storeUserName] TEXT , [createTime] TEXT , " +
//                    " [creatorId] TEXT ,  [creatorName] TEXT ,  [isSuccess] TEXT NOT NULL,  [zfbAmount] TEXT,  [userCount] LONG , [zfType] TEXT , [CUSTOMER_ADDRESS] TEXT)";
//            db.execSQL(sql);
//            db.execSQL(sql2);
//            db.execSQL(trade_consumption_main);
//            db.execSQL(ara_store_log);
            final int version = db.getVersion();
            if (version != mNewVersion) {
                if (db.isReadOnly()) {
                    throw new SQLiteException("Can't upgrade read-only database from version " +  db.getVersion() + " to " + mNewVersion + ": " + mName);
                }
                db.beginTransaction();
                try {
                    if (version == 0) {
                        // 第一次创建数据库调用onCreate方法
                        onCreate(db);
                    } else {
                        if (version > mNewVersion) {
                            // 数据库版本大于当前版本,执行降级操作
                            onDowngrade(db, version, mNewVersion);
                        } else {
                            // 数据库版本小于当前版本,执行升级操作
                            onUpgrade(db, version, mNewVersion);
                        }
                    }
                    db.setVersion(mNewVersion);
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }
            }

            onOpen(db);

            if (db.isReadOnly()) {
                Log.w(TAG, "Opened " + mName + " in read-only mode");
            }

            mDatabase = db;
            return db;
        } finally {
            mIsInitializing = false;
            if (db != null && db != mDatabase) {
                db.close();
            }
        }
    }
    public File getDatabasePath(String name) {
        String EXTERN_PATH = null;
        if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED) == true) {
            String dbPath = mContext.getString(R.string.dir) + mContext.getString(R.string.db_dir) + "/";
            //dbPath = "/cn.aramark.fmmsm"+ mContext.getString(R.string.db_dir)+"/";

            EXTERN_PATH = Environment.getExternalStorageDirectory().getAbsolutePath()+ dbPath;
            File f = new File(EXTERN_PATH);
            if (!f.exists()) {
                f.mkdirs();
            }
        }
        return new File(EXTERN_PATH + name);
    }
    /**
     * Close any open database object.
     */
    public synchronized void close() {
        if (mIsInitializing) throw new IllegalStateException("Closed during initialization");

        if (mDatabase != null && mDatabase.isOpen()) {
            mDatabase.close();
            mDatabase = null;
        }
    }

    /**
     * Called when the database connection is being configured, to enable features
     * such as write-ahead logging or foreign key support.
     * <p>
     * This method is called before {@link #onCreate}, {@link #onUpgrade},
     * {@link #onDowngrade}, or {@link #onOpen} are called.  It should not modify
     * the database except to configure the database connection as required.
     * </p><p>
     * This method should only call methods that configure the parameters of the
     * database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
     * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled},
     * {@link SQLiteDatabase#setLocale}, {@link SQLiteDatabase#setMaximumSize},
     * or executing PRAGMA statements.
     * </p>
     *
     * @param db The database.
     */
    public void onConfigure(SQLiteDatabase db) {}

    /**
     * Called when the database is created for the first time. This is where the
     * creation of tables and the initial population of the tables should happen.
     *
     * @param db The database.
     */
    public abstract void onCreate(SQLiteDatabase db);

    /**
     * Called when the database needs to be upgraded. The implementation
     * should use this method to drop tables, add tables, or do anything else it
     * needs to upgrade to the new schema version.
     *
     * <p>
     * The SQLite ALTER TABLE documentation can be found
     * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
     * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
     * you can use ALTER TABLE to rename the old table, then create the new table and then
     * populate the new table with the contents of the old table.
     * </p><p>
     * This method executes within a transaction.  If an exception is thrown, all changes
     * will automatically be rolled back.
     * </p>
     *
     * @param db The database.
     * @param oldVersion The old database version.
     * @param newVersion The new database version.
     */
    public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);

    /**
     * Called when the database needs to be downgraded. This is strictly similar to
     * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
     * However, this method is not abstract, so it is not mandatory for a customer to
     * implement it. If not overridden, default implementation will reject downgrade and
     * throws SQLiteException
     *
     * <p>
     * This method executes within a transaction.  If an exception is thrown, all changes
     * will automatically be rolled back.
     * </p>
     *
     * @param db The database.
     * @param oldVersion The old database version.
     * @param newVersion The new database version.
     */
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        throw new SQLiteException("Can't downgrade database from version " +
                oldVersion + " to " + newVersion);
    }

    /**
     * Called when the database has been opened.  The implementation
     * should check {@link SQLiteDatabase#isReadOnly} before updating the
     * database.
     * <p>
     * This method is called after the database connection has been configured
     * and after the database schema has been created, upgraded or downgraded as necessary.
     * If the database connection must be configured in some way before the schema
     * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
     * </p>
     *
     * @param db The database.
     */
    public void onOpen(SQLiteDatabase db) {}
}
